Recently I came over a great visualization of the relationships between classes made by Mike Bostock with his Hierarchical Edge Bundling in D3:
I wondered how hard it would be to reimplement this visualization with jQAssistant and Neo4j and show actual dependencies between Java types. So let's have a look!
If you've scanned your project with jQAssistant, you get some graph data like the following:
The graph contains the information about the dependencies of all software entities like Java classes or interfaces. This information is exactly what we need to create a Dependency Analysis between Java types. We just have to create the right Cypher query and the result that fits into the D3 visualization above.
What you need to do is to scan your software with jQAssistant to get the information about the dependencies between classes. Just download the demo project at https://github.com/buschmais/spring-petclinic, build it with Java & Maven (mvn clean install
) and start the embedded Neo4j graph database with mvn jqassistant:server
.
With a running Neo4j database, we connect to it via py2neo and get the relationship information between nodes. In this example, we want to retrieve the direct dependency between any Java type that belongs to our application:
type
's by using only artifacts that are contained in our project by using the corresponding node labels Project
and Artifact
.test-jar
.DEPENDS_ON
relationship to the other types of our application. For reasons of completeness, we also provide the types that don't depend on any other type of our application. That's why we specify the *0..1
parameter in the relationship.COLLECT
all the dependencies for one type into a list (more exact, the fqn
aka full qualified name of the type), because that's what the D3 visualization needs as input. The result is a dictionary with all the information needed for the D3 visualization.
In [1]:
import py2neo
query="""
MATCH
(:Project)-[:CONTAINS]->(artifact:Artifact)-[:CONTAINS]->(type:Type)
WHERE
// we don't want to analyze test artifacts
NOT artifact.type = "test-jar"
WITH DISTINCT type, artifact
MATCH
(type)-[:DEPENDS_ON*0..1]->(directDependency:Type)<-[:CONTAINS]-(artifact)
RETURN type.fqn as name, COLLECT(DISTINCT directDependency.fqn) as imports
"""
json_data = py2neo.Graph().run(query).data()
json_data[:3]
Out[1]:
We just write that data to a file that we read into our D3 HTML-Template.
In [2]:
import json
with open ( "vis/flare-imports.json", mode='w') as json_file:
json_file.write(json.dumps(json_data, indent=3))
That's it: The result is an interactive bundle diagram with all the dependencies between Java types of the application!
In the figure above, I've hovered over the class OwnerRepository
and I can immediately see the types that this class depends on (red) and the types that depend on the class OwnerRepository
(green).
Next: You can also query your software on different abstraction levels. As a proof of concept, I did that for dependencies between packages already:
Stay tuned for more Dependency Analysis in a follow-up post!